//	Project: Handle Template Library
//	File:	 CModule.h
//	Author:  Paul Bludov
//	Date:	 2003-06-06
//
//	Description:
//		C++ wrapper for Win32 Module
//
//	Update History:
//		NONE
//
//@//////////////////////////////////////////////////////////////////////////

#ifndef __HTLMODULE_H__
#define __HTLMODULE_H__

#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000

#pragma warning(push)
#pragma warning(disable: 4127) // conditional expression is constant

/////////////////////////////////////////////////////////////////////////////
// Classes in this file
//
// CModuleT<t_bManaged>
//

namespace HTL
{

/////////////////////////////////////////////////////////////////////////////

inline bool IsValidModule
	( IN HMODULE h
	)
{
#ifdef _DEBUG
	if (NULL != h)
	{
		DWORD nError = ::GetLastError();
		TCHAR szModuleNameFirstChar[2];
		_ASSERTE(::GetModuleFileName(h, szModuleNameFirstChar, 2));
		::SetLastError(nError);
	}
#endif
	return NULL != h;
}

// AddRef the Module in the current process
inline BOOL DuplicateModule
	( IN HMODULE hSource
	, OUT HMODULE *lpDest
	)
{
	_ASSERTE(IsValidModule(hSource));
	_ASSERTE(lpDest);

#if _WIN32_WINNT > 0x0500 || defined(WINBASE_DECLARE_GET_MODULE_HANDLE_EX) || ISOLATION_AWARE_ENABLED
	return ::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCTSTR)hSource, lpDest);
#else
	TCHAR	szFileName[MAX_PATH];

	if (::GetModuleFileName(hSource, szFileName, MAX_PATH))
		return FALSE;

	return (*lpDest = ::LoadLibrary(szFileName)) != NULL;
#endif
}

inline HMODULE LoadLibraryRelativeToModule
	( IN HMODULE hSource
	, IN LPCSTR lpLibFileName
	)
{
	CHAR azFilePath[MAX_PATH];

	if (::GetModuleFileNameA(hSource, azFilePath, MAX_PATH))
	{
		LPSTR		azFileName;
		INT_PTR		nLen;	
			
		azFileName = ::strrchr(azFilePath, '\\');
		if (azFileName)
			++azFileName;
		else
			azFileName = azFilePath;

		nLen = MAX_PATH - (azFileName - azFilePath);
		::lstrcpynA(azFileName, lpLibFileName, (int)nLen);
		return ::LoadLibraryA(azFilePath);
	}
	/*else*/
	return NULL;
}

inline HMODULE LoadLibraryRelativeToModule
	( IN HMODULE hSource
	, IN LPCWSTR lpLibFileName
	)
{
	WCHAR wzFilePath[MAX_PATH];

	if (::GetModuleFileNameW(hSource, wzFilePath, MAX_PATH))
	{
		LPWSTR		wzFileName;
		INT_PTR		nLen;	
			
		wzFileName = ::wcsrchr(wzFilePath, L'\\');
		if (wzFileName)
			++wzFileName;
		else
			wzFileName = wzFilePath;

		nLen = MAX_PATH - (wzFileName - wzFilePath);
		::lstrcpynW(wzFileName, lpLibFileName, (int)nLen);
		return ::LoadLibraryW(wzFilePath);
	}
	/*else*/
	return NULL;
}

/////////////////////////////////////////////////////////////////////////////

//@//////////////////////////////////////////////////////////////////////////
// CModuleT
// A Module wrapper class

template<bool t_bManaged>
class CModuleT
{
public:
// Data members
	HMODULE m_h;

// Constructor/destructor/operators
	CModuleT()
		: m_h(NULL)
	{ }

	explicit CModuleT
		( IN HMODULE h
		)
		: m_h(h)
	{
		_ASSERTE(IsValid());
	}

	CModuleT
		( const CModuleT<true>& rh
		)
		: m_h(NULL)
	{
		// Avoid using this method,
		// use Attach(other.Detach() || Duplicate(other) instead

		if (false == t_bManaged)
			m_h = rh.m_h;
		else if (rh.IsValid() && FALSE == DuplicateFrom(rh.m_h))
			m_h = NULL;
	}

	CModuleT
		( const CModuleT<false>& rh
		)
		: m_h(NULL)
	{
		// Avoid using this method,
		// use Attach(other.Detach() || Duplicate(other) instead

		if (false == t_bManaged)
			m_h = rh.m_h;
		else if (rh.IsValid() && FALSE == DuplicateFrom(rh.m_h))
			m_h = NULL;
	}

	explicit CModuleT
		( IN LPCSTR lpLibFileName
		)
		: m_h(NULL)
	{
		if (t_bManaged)
			LoadLibrary(lpLibFileName);
		else
			GetHandle(lpLibFileName);
	}

	explicit CModuleT
		( IN LPCWSTR lpLibFileName
		)
		: m_h(NULL)
	{
		if (t_bManaged)
			LoadLibrary(lpLibFileName);
		else
			GetHandle(lpLibFileName);
	}

	CModuleT
		( IN HMODULE hSource
		, IN LPCSTR lpLibFileName
		)
		: m_h(NULL)
	{
		LoadLibraryRelativeToModule(hSource, lpLibFileName);
	}

	CModuleT
		( IN HMODULE hSource
		, IN LPCWSTR lpLibFileName
		)
		: m_h(NULL)
	{
		LoadLibraryRelativeToModule(hSource, lpLibFileName);
	}

	~CModuleT()
	{
		if (t_bManaged)
			Close();
	}

	bool IsValid() const
	{
		return HTL::IsValidModule(m_h);
	}

	void Close()
	{
		// Close the Module
		if (IsValid())
		{
			BOOL b;
			b = ::FreeLibrary(m_h);
			_ASSERTE(b);
		}
		m_h = NULL;
	}

	bool operator!() const
	{
		return !IsValid();
	}

	operator HMODULE() const
	{
		return m_h;
	}

	operator bool() const
	{
		return IsValid();
	}

	HMODULE* operator&()
	{
		// The assert on operator& usually indicates a bug.	If this is really
		// what is needed, take the address of the m_h member explicitly

		_ASSERTE(!IsValid());
		return &m_h;
	}

	CModuleT<t_bManaged>& operator=(HMODULE h)
	{
		// Try m_Module.Attach(other.Detach())
		// if you got an assertion here.

		_ASSERTE(!IsValid() || false == t_bManaged);
		m_h = h;
		return *this;
	}

	CModuleT<t_bManaged>& operator=(const CModuleT<true>& other)
	{
		// Avoid using this method,
		// use Attach(other.Detach() || Duplicate(other) instead

		_ASSERTE(!IsValid() || false == t_bManaged);
		if (false == t_bManaged)
			m_h = other.m_h;
		else if (!other.IsValid() || FALSE == DuplicateFrom(other.m_h))
			m_h = NULL;
		return *this;
	}

	CModuleT<t_bManaged>& operator=(const CModuleT<false>& other)
	{
		// Avoid using this method,
		// use Attach(other.Detach() || Duplicate(other) instead

		_ASSERTE(!IsValid() || false == t_bManaged);
		if (false == t_bManaged)
			m_h = other.m_h;
		else if (!other.IsValid() || FALSE == DuplicateFrom(other.m_h))
			m_h = NULL;
		return *this;
	}

	void Attach
		( IN HMODULE h
		)
	{
		// The main difference between operator= & Attach
		// that Attach releases the Module.

		if (t_bManaged && IsValid())
			::FreeLibrary(m_h);
		m_h = h;
	}

	HMODULE Detach()
	{
		HMODULE h = m_h;
		m_h = NULL;
		return h;
	}

	BOOL DuplicateFrom
		( IN HMODULE hSource
		)
	{
		_ASSERTE(IsValidModule(hSource));
		_ASSERTE(!IsValid());

		return HTL::DuplicateModule(hSource, &m_h);
	}

	BOOL DuplicateTo
		( OUT HMODULE lphDest
		) const
	{
		_ASSERTE(lphDest);
		_ASSERTE(IsValid());

		return HTL::DuplicateModule(m_h, lphDest);
	}

	HMODULE LoadLibraryRelativeToModule
		( IN HMODULE hSource
		, IN LPCSTR lpLibFileName
		)
	{
		_ASSERTE(!IsValid());

		m_h = HTL::LoadLibraryRelativeToModule(hSource, lpLibFileName);
		return m_h;
	}

	HMODULE LoadLibraryRelativeToModule
		( IN HMODULE hSource
		, IN LPCWSTR lpLibFileName
		)
	{
		_ASSERTE(!IsValid());

		m_h = HTL::LoadLibraryRelativeToModule(hSource, lpLibFileName);
		return m_h;
	}

	BOOL FreeLibrary()
	{
		_ASSERTE(IsValid());

		HMODULE		h = m_h;

		m_h = NULL;
		return ::FreeLibrary(h);
	}

	BOOL FreeLibraryAndExitThread
		( IN DWORD nExitCode
		)
	{
		_ASSERTE(IsValid());

		HMODULE		h = m_h;

		m_h = NULL;
		return ::FreeLibraryAndExitThread(h, nExitCode);
	}

	BOOL DisableThreadLibraryCalls()
	{
		_ASSERTE(IsValid());

		return ::DisableThreadLibraryCalls(m_h);
	}

	FARPROC GetProcAddress
		( IN LPCSTR lpProcName
		)
	{
		_ASSERTE(IsValid());

		return ::GetProcAddress(m_h, lpProcName);
	}

	FARPROC GetProcAddress
		( IN LPCWSTR lpProcName
		)
	{
		_ASSERTE(IsValid());

		if (IS_INTRESOURCE(lpProcName))
			return ::GetProcAddress(m_h, (LPCSTR)lpProcName);

		int		nLen = ::lstrlenW(lpProcName) + 1;
		LPSTR	lpProcNameAnsi = (LPSTR)_alloca(nLen * sizeof(CHAR));

		lpProcNameAnsi[0] = '\x0';
		if (::WideCharToMultiByte(CP_ACP, 0, lpProcName, -1, lpProcNameAnsi, nLen, NULL, NULL))
			return ::GetProcAddress(m_h, lpProcNameAnsi);

		/*else*/
		return NULL;
	}

	HGLOBAL LoadResource
		( IN HRSRC hResInfo
		)
	{
		_ASSERTE(IsValid());

		return ::LoadResource(m_h, hResInfo);
	}

	DWORD SizeofResource
		( IN HRSRC hResInfo
		)
	{
		_ASSERTE(IsValid());

		return ::SizeofResource(m_h, hResInfo);
	}

	HMODULE LoadLibrary
		( IN LPCSTR lpLibFileName
		)
	{
		_ASSERTE(!IsValid());

		m_h = ::LoadLibraryA(lpLibFileName);
		return m_h;
	}

	HMODULE LoadLibrary
		( IN LPCWSTR lpLibFileName
		)
	{
		_ASSERTE(!IsValid());

		m_h = ::LoadLibraryW(lpLibFileName);
		return m_h;
	}

	HMODULE LoadLibraryEx
		( IN LPCSTR lpLibFileName
		, IN HANDLE hFile
		, IN DWORD nFlags
		)
	{
		_ASSERTE(!IsValid());

		m_h = ::LoadLibraryExA(lpLibFileName, hFile, nFlags);
		return m_h;
	}

	HMODULE LoadLibraryEx
		( IN LPCWSTR lpLibFileName
		, IN HANDLE hFile
		, IN DWORD nFlags
		)
	{
		_ASSERTE(!IsValid());

		m_h = ::LoadLibraryExW(lpLibFileName, hFile, nFlags);
		return m_h;
	}

	DWORD GetFileName
		( OUT LPSTR lpFilename
		, IN DWORD nSize
		)
	{
		_ASSERTE(IsValid());

		return ::GetModuleFileNameA(m_h, lpFilename, nSize);
	}

	DWORD GetFileName
		( OUT LPWSTR lpFilename
		, IN DWORD nSize
		)
	{
		_ASSERTE(IsValid());

		return ::GetModuleFileNameW(m_h, lpFilename, nSize);
	}

	HMODULE GetHandle
		( IN LPCSTR lpLibFileName
		)
	{
		_ASSERTE(!IsValid());

		m_h = ::GetModuleHandleA(lpLibFileName);
		return m_h;
	}

	HMODULE GetHandle
		( IN LPCWSTR lpLibFileName
		)
	{
		_ASSERTE(!IsValid());

		m_h = ::GetModuleHandleW(lpLibFileName);
		return m_h;
	}

#if _WIN32_WINNT > 0x0500 || defined(WINBASE_DECLARE_GET_MODULE_HANDLE_EX) || ISOLATION_AWARE_ENABLED
	BOOL GetHandleEx
		( IN DWORD nFlags
		, IN LPCSTR lpModuleName
		)
	{
		_ASSERTE(!IsValid());

		return ::GetModuleHandleExA(nFlags, lpModuleName, &m_h);
	}

	BOOL GetHandleEx
		( IN DWORD nFlags
		, IN LPCWSTR lpModuleName
		)
	{
		_ASSERTE(!IsValid());

		return ::GetModuleHandleExW(nFlags, lpModuleName, &m_h);
	}
#endif //_WIN32_WINNT > 0x0500 || defined(WINBASE_DECLARE_GET_MODULE_HANDLE_EX) || ISOLATION_AWARE_ENABLED

	HRSRC FindResource
		( IN LPCSTR lpName
		, IN LPCSTR lpType
		)
	{
		_ASSERTE(IsValid());

		return ::FindResourceA(m_h, lpName, lpType);
	}

	HRSRC FindResource
		( IN LPCWSTR lpName
		, IN LPCWSTR lpType
		)
	{
		_ASSERTE(IsValid());

		return ::FindResourceW(m_h, lpName, lpType);
	}

	HRSRC FindResourceEx
		( IN LPCSTR lpType
		, IN LPCSTR lpName
		, IN WORD nLanguage
		)
	{
		_ASSERTE(IsValid());

		return ::FindResourceExA(m_h, lpName, lpType, nLanguage);
	}

	HRSRC FindResourceEx
		( IN LPCWSTR lpType
		, IN LPCWSTR lpName
		, IN WORD nLanguage
		)
	{
		_ASSERTE(IsValid());

		return ::FindResourceExW(m_h, lpName, lpType, nLanguage);
	}

	BOOL EnumResourceTypes
		( IN ENUMRESTYPEPROCA lpEnumFunc
		, IN LONG_PTR lParam
		)
	{
		_ASSERTE(IsValid());

		return ::EnumResourceTypesA(m_h, lpEnumFunc, lParam);
	}

	BOOL EnumResourceTypes
		( IN ENUMRESTYPEPROCW lpEnumFunc
		, IN LONG_PTR lParam
		)
	{
		_ASSERTE(IsValid());

		return ::EnumResourceTypesW(m_h, lpEnumFunc, lParam);
	}

	BOOL EnumResourceNames
		( IN LPCSTR lpType
		, IN ENUMRESNAMEPROCA lpEnumFunc
		, IN LONG_PTR lParam
		)
	{
		_ASSERTE(IsValid());

		return ::EnumResourceNamesA(m_h, lpType, lpEnumFunc, lParam);
	}

	BOOL EnumResourceNames
		( IN LPCWSTR lpType
		, IN ENUMRESNAMEPROCW lpEnumFunc
		, IN LONG_PTR lParam
		)
	{
		_ASSERTE(IsValid());

		return ::EnumResourceNamesW(m_h, lpType, lpEnumFunc, lParam);
	}

	BOOL EnumResourceLanguages
		( IN LPCSTR lpType
		, IN LPCSTR lpName
		, IN ENUMRESLANGPROCA lpEnumFunc
		, IN LONG_PTR lParam
		)
	{
		_ASSERTE(IsValid());

		return ::EnumResourceLanguagesA(m_h, lpType, lpName, lpEnumFunc, lParam);
	}

	BOOL EnumResourceLanguages
		( IN LPCWSTR lpType
		, IN LPCWSTR lpName
		, IN ENUMRESLANGPROCW lpEnumFunc
		, IN LONG_PTR lParam
		)
	{
		_ASSERTE(IsValid());

		return ::EnumResourceLanguagesW(m_h, lpType, lpName, lpEnumFunc, lParam);
	}

#if defined(_PSAPI_H_)
	DWORD GetBaseName
		( IN HANDLE hProcess
		, OUT LPSTR lpBaseName
		, IN DWORD nSize
		)
	{
		_ASSERTE(IsValid());

		return ::GetModuleBaseNameA(hProcess, m_h, lpBaseName, nSize);
	}

	DWORD GetBaseName
		( IN HANDLE hProcess
		, OUT LPWSTR lpBaseName
		, IN DWORD nSize
		)
	{
		_ASSERTE(IsValid());

		return ::GetModuleBaseNameW(hProcess, m_h, lpBaseName, nSize);
	}

	DWORD GetFileNameEx
		( IN HANDLE hProcess
		, OUT LPSTR lpFilename
		, IN DWORD nSize
		)
	{
		_ASSERTE(IsValid());

		return ::GetModuleFileNameExA(hProcess, m_h, lpFilename, nSize);
	}

	DWORD GetFileNameEx
		( IN HANDLE hProcess
		, OUT LPWSTR lpFilename
		, IN DWORD nSize
		)
	{
		_ASSERTE(IsValid());

		return ::GetModuleFileNameExW(hProcess, m_h, lpFilename, nSize);
	}

	BOOL GetInformation
		( IN HANDLE hProcess
		, OUT LPMODULEINFO lpmodinfo
		, IN DWORD nSize
		)
	{
		_ASSERTE(IsValid());

		return ::GetModuleInformation(hProcess, m_h, lpmodinfo, nSize);
	}
#endif // defined(_PSAPI_H_)

};

typedef CModuleT<true> CModule;
typedef CModuleT<false> CModuleHandle;

/////////////////////////////////////////////////////////////////////////////

}	// namespace HTL

/////////////////////////////////////////////////////////////////////////////

#pragma warning(pop)

#endif	// #ifndef __HTLMODULE_H__

/////////////////////////////////////////////////////////////////////////////
//	End Of File htlmodule.h
